home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1997 January
/
CHIP Turkiye Ocak 1997.iso
/
program
/
sound
/
amod30
/
modunit.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1995-08-11
|
48KB
|
1,495 lines
{AdnMod 0.30 by Beta/Adrenalin.
GUS only
Thanks to:
Gravis for great soundcard
flap / Capacala for sending me "some" info
Mark Feldman for PCGPE
Mark Dixon for his GUS669 source
Thunder for excellent info about MODs
Tran & Joshua C. Jensen for releasing ultradox
Greets:
Wihannes / Nordic vision
Solar / Hysteria
sshadow / a-men pc
Wog / a-men pc
Psyko / Acidface software
ASYLUM.ZIP
All users of Metropoli & Starport
}
unit modunit;
{$s-}
interface
uses dos,modtypes;
const
maxchn = 14; {max # of channels in mod.}
amp_vol : byte = 11; {amplifying volume. Increasing by one doubles
the volume}
def_pan : byte = 4; {default panning. 0-7}
max_per = 900; {Max & min period for Amiga limits}
min_per = 20; {not implemented anymore because of extra octaves}
Base : word = $200; {GUS address}
ramp_speed = 63;
mod_error : word = 0;
{0 = no error
1 = too many channels
2 = load error
3 = out of pattern memory
255 = other error}
{1536 bytes}
per_table : array[0..15,1..48] of word = (
(856,808,762,720,678,640,604,570,538,508,480,453,
428,404,381,360,339,320,302,285,269,254,240,226,
214,202,190,180,170,160,151,143,135,127,120,113,
107,101,95,90,85,80,75,71,67,63,60,56),
(850,802,757,715,674,637,601,567,535,505,477,450,{ : C-1 to B-1 Finetune +1}
425,401,379,357,337,318,300,284,268,253,239,225, { : C-2 to B-2 Finetune +1}
213,201,189,179,169,159,150,142,134,126,119,113, { : C-3 to B-3 Finetune +1}
106,100,94,89,84,79,75,71,67,83,59,56), { : C-4 to B-4 Finetune +1}
(844,796,752,709,670,632,597,563,532,502,474,447,{ : C-1 to B-1 Finetune +2}
422,398,376,355,335,316,298,282,266,251,237,224, { : C-2 to B-2 Finetune +2}
211,199,188,177,167,158,149,141,133,125,118,112, { : C-3 to B-3 Finetune +2}
105, 99, 94, 88, 83, 79, 74, 70, 66, 62, 59, 56),{ : C-4 to B-4 Finetune +2}
(838,791,746,704,665,628,592,559,528,498,470,444,{ : C-1 to B-1 Finetune +3}
419,395,373,352,332,314,296,280,264,249,235,222, { : C-2 to B-2 Finetune +3}
209,198,187,176,166,157,148,140,132,125,118,111, { : C-3 to B-3 Finetune +3}
104, 99, 93, 88, 83, 78, 74, 70, 66, 62, 59, 55),{ : C-4 to B-4 Finetune +3}
(832,785,741,699,660,623,588,555,524,495,467,441,{ : C-1 to B-1 Finetune +4}
416,392,370,350,330,312,294,278,262,247,233,220, { : C-2 to B-2 Finetune +4}
208,196,185,175,165,156,147,139,131,124,117,110, { : C-3 to B-3 Finetune +4}
104, 98, 92, 87, 82, 78, 73, 69, 65, 62, 58, 55),{ ; C-4 to B-4 Finetune +4}
(826,779,736,694,655,619,584,551,520,491,463,437,{ : C-1 to B-1 Finetune +5}
413,390,368,347,328,309,292,276,260,245,232,219, { : C-2 to B-2 Finetune +5}
206,195,184,174,164,155,146,138,130,123,116,109, { : C-3 to B-3 Finetune +5}
103, 97, 92, 87, 82, 77, 73, 69, 65, 61, 58, 54),{ ; C-4 to B-4 Finetune +5}
(820,774,730,689,651,614,580,547,516,487,460,434,{ : C-1 to B-1 Finetune +6}
410,387,365,345,325,307,290,274,258,244,230,217, { : C-2 to B-2 Finetune +6}
205,193,183,172,163,154,145,137,129,122,115,109, { : C-3 to B-3 Finetune +6}
102, 96, 91, 86, 81, 77, 72, 68, 64, 61, 57, 54),{ : C-4 to B-4 Finetune +6}
(814,768,725,684,646,610,575,543,513,484,457,431,{ : C-1 to B-1 Finetune +7}
407,384,363,342,323,305,288,272,256,242,228,216, { : C-2 to B-2 Finetune +7}
204,192,181,171,161,152,144,136,128,121,114,108, { : C-3 to B-3 Finetune +7}
102, 96, 90, 85, 80, 76, 72, 68, 64, 60, 57, 54),{ : C-4 to B-4 Finetune +7}
(907,856,808,762,720,678,640,604,570,538,504,480,{ : C-1 to B-1 Finetune -8}
453,428,404,381,360,339,320,302,285,269,254,240, { : C-2 to B-2 Finetune -8}
226,214,202,190,180,170,160,151,143,135,127,120, { : C-3 to B-3 Finetune -8}
113,107,101, 95, 90, 85, 80, 75, 71, 67, 63, 60),{ : C-4 to B-4 Finetune -8}
(900,850,802,757,715,675,636,601,567,535,505,477,{ : C-1 to B-1 Finetune -7}
450,425,401,379,357,337,318,300,284,268,253,238, { : C-2 to B-2 Finetune -7}
225,212,200,189,179,169,159,150,142,134,126,119, { : C-3 to B-3 Finetune -7}
112,106,100, 94, 89, 84, 79, 75, 71, 67, 63, 59),{ : C-4 to B-4 Finetune -7}
(894,844,796,752,709,670,632,597,563,532,502,474,{ : C-1 to B-1 Finetune -6}
447,422,398,376,355,335,316,298,282,266,251,237, { : C-2 to B-2 Finetune -6}
223,211,199,188,177,167,158,149,141,133,125,118, { : C-3 to B-3 Finetune -6}
111,105, 99, 94, 88, 83, 79, 74, 70, 66, 62, 59),{ : C-4 to B-4 Finetune -6}
(887,838,791,746,704,665,628,592,559,528,498,470,{ : C-1 to B-1 Finetune -5}
444,419,395,373,352,332,314,296,280,264,249,235, { : C-2 to B-2 Finetune -5}
222,209,198,187,176,166,157,148,140,132,125,118, { : C-3 to B-3 Finetune -5}
111,104, 99, 93, 88, 83, 78, 74, 70, 66, 62, 59),{ : C-4 to B-4 Finetune -5}
(881,832,785,741,699,660,623,588,555,524,494,467,{ : C-1 to B-1 Finetune -4}
441,416,392,370,350,330,312,294,278,262,247,233, { : C-2 to B-2 Finetune -4}
220,208,196,185,175,165,156,147,139,131,123,117, { : C-3 to B-3 Finetune -4}
110,104, 98, 92, 87, 82, 78, 73, 69, 65, 61, 58),{ : C-4 to B-4 Finetune -4}
(875,826,779,736,694,655,619,584,551,520,491,463,{ : C-1 to B-1 Finetune -3}
437,413,390,368,347,338,309,292,276,260,245,232, { : C-2 to B-2 Finetune -3}
219,206,195,184,174,164,155,146,138,130,123,116, { : C-3 to B-3 Finetune -3}
109,103, 97, 92, 87, 82, 77, 73, 69, 65, 61, 58),{ : C-4 to B-4 Finetune -3}
(868,820,774,730,689,651,614,580,547,516,487,460,{ : C-1 to B-1 Finetune -2}
434,410,387,365,345,325,307,290,274,258,244,230, { : C-2 to B-2 Finetune -2}
217,205,193,183,172,163,154,145,137,129,122,115, { : C-3 to B-3 Finetune -2}
108,102, 96, 91, 86, 81, 77, 72, 68, 64, 61, 57),{ : C-4 to B-4 Finetune -2}
(862,814,768,725,684,646,610,575,543,513,484,457,{ : C-1 to B-1 Finetune -1}
431,407,384,363,342,323,305,288,272,256,242,228, { : C-2 to B-2 Finetune -1}
216,203,192,181,171,161,152,144,136,128,121,114, { : C-3 to B-3 Finetune -1}
108,101, 96, 90, 85, 80, 76, 72, 68, 64, 60, 57));{: C-4 to B-4 Finetune -1}
gusvol : array[0..64] of word =
(0,1246,1502,1646,1758,1846,1902,1958,2014,2070,
2102,2130,2158,2186,2214,2242,2270,2298,2326,2344,
2358,2372,2386,2400,2414,2428,2442,2456,2470,2484,
2498,2512,2526,2540,2554,2568,2582,2593,2600,2607,
2614,2621,2628,2635,2642,2649,2656,2663,2670,2677,
2684,2691,2698,2705,2712,2719,2726,2733,2740,2747,
2754,2761,2768,2775,2782);
vib_tbl : array[0..2,0..63] of shortint = {192 bytes}
((0,6,12,19,24,30,36,41,45,49,53,56,59,61,63,64,
64,64,63,61,59,56,53,49,45,41,36,30,24,19,12,6,
0,-6,-12,-19,-24,-30,-36,-41,-45,-49,-53,-56,-59,-61,-63,-64,
-64,-64,-63,-61,-59,-56,-53,-49,-45,-41,-36,-30,-24,-19,-12,-6),
(-63,-61,-59,-57,-55,-53,-51,-49,-47,-45,-43,-41,-39,-37,-35,-33,
-31,-29,-27,-25,-23,-21,-19,-17,-15,-13,-11,-9,-7,-5,-3,-1,
1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,
33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63),
(-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64));
type
t_channel = record
Vol : byte; {current volume 0-64}
note : byte; {current note 1(C-1) to 48(B-4)}
Per,dper : word; {period & dest. period for tone portamentos}
Sample : byte; {current sample}
Pan : byte; {panning}
fx,fxdata : byte;
fx_sl2,fx_vib : byte; {slide to & vibrato fx-data}
fx_portd,fx_portu : byte; {slide up & down fx-data}
fx_trm : byte; {tremolo fx-data}
vib_wave : byte; {vibrato waveform}
vib_cnt : byte; {vibrato counter}
trig_cnt : byte; {retrig counter}
arp1,arp2, {arpeggio params}
arp_cnt : byte; {arpeggio counter}
start_fx : byte; {tick to start do_fx for channel}
on : byte; {0 = channel is muted}
bar : byte; {volume bar}
hit : byte;
no_fx : byte; {1 = do not get new fx}
gvol : word;
end;
t_sample = record
Name : array[1..23] of char;
Addr : longint; {address in GUS mem}
Length : word;
LoopStart,
LoopEnd : word;
ftune : byte;
Volume : byte;
end;
t_note = record
per : word;
note,
sample,
fx,
fxdata : byte;
end;
t_pattern = array[0..(64*14)-1] of t_note;
p_pattern = ^t_pattern;
mod_header = record
name : string[20];
Length : integer;
tag : array[0..3] of char; {M.K.}
chns : integer; {1..14}
samples : integer; {15 / 31}
end;
var
gus_addr : array[0..32] of longint; {128 bytes}
periods : array[0..1100] of word; {2200 bytes}
channels : array[0..maxchn-1] of t_channel;
samples : array[0..32] of t_sample; {1120 bytes}
patterns : array[0..128] of p_pattern; {516 bytes}
orders : array[0..255] of byte; {order list}
max_ptn : word; {# patterns in mod}
cur_ptn,cur_row,cur_tick : byte;
new_ptn,new_row,jump : byte; {used in jumps}
speed,nspeed,tempo : integer;
vblank : boolean; {true = do not use bpm tempos}
playing,loaded : boolean; {guess :-)}
header : mod_header;
top_addr : longint; {Next free address in GUS mem}
time_counter : longint; {For syncing with demos. Increments
every 1/18.2 seconds}
time_counter2 : longint; {Increments every tick}
time_counter3 : longint;
Procedure GUSDelay;
Function VoicePos( V : Byte) : Longint;
Function GUSPeek(Loc : Longint) : Byte;
Procedure GUSPoke(Loc : Longint; B : Byte);
Function GUSProbe(adr : word) : Boolean;
Procedure GUSFind;
Function GUSFindMem : Longint;
Procedure GUSSetFreq( V : Byte; hz : Word);
Procedure GUSVoiceControl( V, B : Byte);
Procedure GUSSetBalance( V, Bal : Byte);
Procedure GUSSetVolume( V : Byte; Vol : Word);
Procedure GUSSetLoopMode( V : Byte);
Procedure GUSStopVoice( V : Byte);
Procedure GUSPlayVoice( V, Mode : Byte;VBegin, VStart, VEnd : Longint);
Procedure GUSPlayAll( V, Mode : Byte;VBegin, VStart, VEnd : Longint;
freq,vol : word);
procedure gussetramp(chn,vstart,vend,rate : integer);
procedure gusrelvoice(v : byte);
procedure GusSetOfs(v : byte;vbegin : longint);
Procedure GUSReset;
procedure gusdeinit;
procedure updatenotes;
procedure start_playing;
procedure stop_playing;
procedure set_timer(ticks : word);
procedure init_mod;
procedure free_mod;
procedure load_mod(s : string;debug : boolean);
procedure goto_mod(ptn,row : integer);
implementation
type
t_memarray = array[0..2000] of word;
t_memarray2 = array[0..5000] of byte;
var
oldint : procedure;
int_tick,o_int_tick : word;
timer_rate,timer_cnt,
int_rate : word;
gus_bank : longint;
misc_buf : ^t_memarray2; {buffer used while loading mod}
misc_buf2 : ^t_memarray; {points to misc_buf}
{$i gus.inc}
{$s-}
procedure get_notes;
var
chn : byte;
ptn : byte;
org_sam,sam,note : byte;
st_ofs : longint;
per,dper,vol,freq : word;
_fx,_fxdata : byte;
mute: byte;
_ptn : p_pattern;
ftune : integer;
ovol : word;
procedure prefx;
var
w : word;
_efxdata : byte;
begin
case _fx of
9 : begin
w := _fxdata*$100;
st_ofs := w;
channels[chn].no_fx := 1;
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
end;
$c : begin
if _fxdata > 64 then _fxdata := 64;
vol := _fxdata;
channels[chn].no_fx := 1;
end;
$e : begin
_efxdata := _fxdata and 15;
case _fxdata shr 4 of
4 : begin
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
if _efxdata and 3 < 3 then channels[chn].vib_wave := _efxdata
else channels[chn].vib_wave := 0 or (_efxdata and 4);
end;
$c : if _efxdata and 15 = 0 then begin
mute := 1;
gusstopvoice(chn);
end;
$d : if _efxdata > 0 then mute := 2
else mute := 0;
end;
end;
end;
end;
begin
ptn := orders[cur_ptn];
_ptn := virt_getptn(ptn);
for chn := 0 to header.chns-1 do begin
if channels[chn].fx = 0 then begin
sam := channels[chn].sample;
per := per_table[samples[sam].ftune,
channels[chn].note];
gussetfreq(chn,periods[per]);
end;
channels[chn].hit := 0;
if ((_ptn^[cur_row*header.chns+chn].per > 0) or
(_ptn^[cur_row*header.chns+chn].sample > 0)) then begin
mute := 1;
vol := channels[chn].vol;
per := channels[chn].per;
note := channels[chn].note;
freq := periods[channels[chn].per];
_fx := _ptn^[cur_row*header.chns+chn].fx;
_fxdata := _ptn^[cur_row*header.chns+chn].fxdata;
org_sam := _ptn^[cur_row*header.chns+chn].sample;
channels[chn].start_fx := 0;
channels[chn].trig_cnt := 0;
if org_sam = 0 then begin
sam := channels[chn].sample;
end
else begin
sam := org_sam;
end;
ftune := samples[sam].ftune;
if (_fx = $e) and (_fxdata shr 4 = 5) then ftune := _fxdata and 15;
if (_fx = 3) or (_fx = 5) then begin {port to/port to&vol slide}
mute := 1; {dont restart sample}
if _ptn^[cur_row*header.chns+chn].note > 0 then begin
note := _ptn^[cur_row*header.chns+chn].note;
dper := per_table[ftune,note];
if dper > max_per then dper := max_per;
if dper < min_per then dper := min_per;
channels[chn].dper := dper;
end;
end
else if _ptn^[cur_row*header.chns+chn].per > 0 then begin
if _ptn^[cur_row*header.chns+chn].note > 0 then begin
note := _ptn^[cur_row*header.chns+chn].note;
per := per_table[ftune,note];
end
else if _ptn^[cur_row*header.chns+chn].per > 0 then
per := _ptn^[cur_row*header.chns+chn].per;
if per > max_per then per := max_per;
if per < min_per then per := min_per;
channels[chn].dper := per;
channels[chn].per := per;
freq := periods[per];
mute := 0;
end;
if org_sam > 0 then begin {should I reset volume}
vol := samples[sam].volume;
if channels[chn].sample <> org_sam then mute := 0;
end;
if samples[sam].length > 0 then st_ofs := 2;
{coz first 2 bytes = amiga loopinfo, discard them}
channels[chn].no_fx := 0;
prefx;
channels[chn].vol := vol;
channels[chn].note := note;
if channels[chn].vib_wave and 4 = 0 then channels[chn].vib_cnt := 0;
channels[chn].sample := sam;
channels[chn].bar := channels[chn].vol;
ovol := channels[chn].gvol;
vol := (gusvol[vol]*amp_vol+20000);
channels[chn].gvol := vol;
if st_ofs > samples[sam].length then st_ofs := samples[sam].length;
if channels[chn].on = 0 then mute := 1;
if mute = 0 then begin
channels[chn].hit := 1;
{gussetbalance(chn,channels[chn].pan);}
if (samples[sam].loopend > 2) then
gusplayall(chn,8,gus_addr[sam]+st_ofs,
gus_addr[sam]+samples[sam].loopstart,
gus_addr[sam]+samples[sam].loopend,freq,
20000)
else gusplayall(chn,0,gus_addr[sam]+st_ofs,
gus_addr[sam]+st_ofs,
gus_addr[sam]+samples[sam].length,freq,
20000);
gussetramp(chn,20000 shr 8,vol shr 8,ramp_speed);
end
else if (channels[chn].on = 1) and (mute=1) then begin
gussetramp(chn,ovol shr 8,vol shr 8,ramp_speed);
{gussetvolume(chn,vol);}
end;
end;
end;
end;
procedure get_fx;
var
chn,ptn : byte;
_fx,_fxdata : byte;
_efx,_efxdata : byte;
per : word;
b : byte;
w : word;
_ptn : p_pattern;
begin
ptn := orders[cur_ptn];
_ptn := virt_getptn(ptn);
new_ptn := cur_ptn;
new_row := cur_row;
jump := 0;
for chn := 0 to header.chns-1 do
if channels[chn].no_fx = 0 then begin
_fx := _ptn^[cur_row*header.chns+chn].fx;
_fxdata := _ptn^[cur_row*header.chns+chn].fxdata;
if (_fx=0) and (_fxdata = 0) then _fx := 255;
channels[chn].start_fx := 0;
channels[chn].fx := _fx;
channels[chn].fxdata := _fxdata;
case _fx of
0 : begin {Arpeggio}
channels[chn].arp1 := _fxdata shr 4;
channels[chn].arp2 := _fxdata and 15;
channels[chn].arp_cnt := 0;
end;
1 : begin {port up}
channels[chn].start_fx := 2;
end;
2 : begin {port down}
channels[chn].start_fx := 2;
end;
3 : begin {port to}
if _fxdata > 0 then begin
channels[chn].fxdata := _fxdata;
channels[chn].fx_sl2 := _fxdata;
end
else channels[chn].fxdata := channels[chn].fx_sl2;
channels[chn].start_fx := 2;
end;
4 : begin {vibrato}
b := _fxdata and 15;
if b = 0 then b := channels[chn].fx_vib and 15;
w := b;
b := _fxdata shr 4;
if b = 0 then b := channels[chn].fx_vib shr 4;
w := w or (b shl 4);
b := w;
channels[chn].fxdata := b;
channels[chn].fx_vib := b;
end;
5 : begin {port to & vol slide}
if _fxdata and 15 > 0 then
_fxdata := _fxdata and 15; {if both ways, then slide down}
channels[chn].fxdata := _fxdata;
end;
6 : begin {Vibrato & vol slide}
end;
7 : begin {Tremolo}
if _fxdata > 0 then begin
channels[chn].fxdata := _fxdata;
channels[chn].fx_trm := _fxdata;
end
else channels[chn].fxdata := channels[chn].fx_trm;
end;
8 : begin {Set dmp-panning}
end;
9 : begin {set sample offset}
w := _fxdata * 256;
b := channels[chn].sample;
if channels[chn].on = 1 then gussetofs(chn,gus_addr[b]+w);
end;
$a : begin {volume slide}
if _fxdata and 15 > 0 then
_fxdata := _fxdata and 15; {if both ways, then slide up}
channels[chn].fxdata := _fxdata;
channels[chn].start_fx := 2;
end;
$b : begin {position jump}
if _fxdata < header.length then begin
new_ptn := _fxdata;
new_row := 0;
jump := 1;
end;
end;
$c : begin {Set volume}
if _fxdata > 64 then _fxdata := 64;
channels[chn].fxdata := _fxdata;
channels[chn].vol := _fxdata;
channels[chn].bar := _fxdata;
w := channels[chn].gvol;
channels[chn].gvol := gusvol[_fxdata]*amp_vol+20000;
if channels[chn].on = 1 then begin
gussetvolume(chn,w);
gussetramp(chn,w shr 8,channels[chn].gvol shr 8,ramp_speed);
end;
end;
$d : begin {break pattern}
new_ptn := cur_ptn;
inc(new_ptn);
new_row := ((_fxdata and $f0) shr 4)*10+_fxdata and 15;
jump := 1;
end;
$e : begin {extended effect}
_efx := _fxdata shr 4;
_efxdata := _fxdata and 15;
case _efx of
1 : begin {fine portamento up}
per := channels[chn].per;
inc(per,_efxdata);
if per > max_per then per := max_per;
channels[chn].per := per;
w := periods[channels[chn].per];
gussetfreq(chn,w);
end;
2 : begin {fine portamento down}
per := channels[chn].per;
dec(per,_efxdata);
if per < min_per then per := min_per;
channels[chn].per := per;
w := periods[channels[chn].per];
gussetfreq(chn,w);
end;
4 : begin {set vibrato waveform}
channels[chn].vib_wave := _efxdata;
end;
5 : begin
end;
8 : begin {set mtm-pan}
channels[chn].pan := _efxdata;
gussetbalance(chn,_efxdata);
end;
9 : if _efxdata > 0 then begin {retrigger}
channels[chn].trig_cnt := _efxdata;
end;
$a : begin {fine vol slide up}
b := channels[chn].vol;
inc(b,_efxdata);
if b > 64 then b := 64;
channels[chn].vol := b;
channels[chn].gvol := gusvol[b]*amp_vol+20000;
if channels[chn].on = 1 then
gussetvolume(chn,channels[chn].gvol);
channels[chn].bar := b;
end;
$b : begin {fine vol slide down}
b := channels[chn].vol;
dec(b,_efxdata);
if b > 128 then b := 0;
channels[chn].vol := b;
channels[chn].gvol:= gusvol[b]*amp_vol+20000;
if channels[chn].on = 1 then
gussetvolume(chn,channels[chn].gvol);
channels[chn].bar := b;
end;
$c : begin {cut note}
end;
$d : if _efxdata > 0 then begin {note delay}
channels[chn].start_fx := _efxdata+1;
end
else channels[chn].fx := 255;
end;
end;
$f : begin {set speed}
if (_fxdata <= 32) or vblank then begin {SPEED not tempo}
nspeed := _fxdata;
speed := _fxdata;
end
else begin {Tempo}
tempo := _fxdata;
{timer_rate := 2500 div (tempo);}
asm
mov ax,tempo {round}
shr ax,1
add ax,2500
mov dx,0
mov cx,tempo
div cx
mov timer_rate,ax
end;
{set_timer(int_rate);}
end;
end
else begin
channels[chn].fx := 255;
channels[chn].fxdata := 0;
end;
end;
end
else channels[chn].no_fx := 0;
end;
procedure do_fx;
var
chn : byte;
_fx,_fxdata : byte;
_efx,_efxdata : byte;
per : word;
b : byte;
s : shortint;
w : word;
begin
for chn := 0 to header.chns-1 do if channels[chn].on = 1 then begin
if channels[chn].start_fx > 0 then dec(channels[chn].start_fx);
_fx := channels[chn].fx;
_fxdata := channels[chn].fxdata;
if (channels[chn].on = 1) and (channels[chn].start_fx = 0)
then case _fx of
0 : with channels[chn] do begin {arpeggio}
case channels[chn].arp_cnt mod 3 of
0 : gussetfreq(chn,
periods[per_table[samples[sample].ftune,note]]);
1 : gussetfreq(chn,
periods[per_table[samples[sample].ftune,note+arp1]]);
2 : gussetfreq(chn,
periods[per_table[samples[sample].ftune,note+arp2]]);
end;
inc(arp_cnt);
end;
1 : begin {port up}
per := channels[chn].per;
dec(per,_fxdata);
if per < min_per then per := min_per;
channels[chn].per := per;
gussetfreq(chn,periods[per]);
end;
2 : begin {port down}
per := channels[chn].per;
inc(per,_fxdata);
if per > max_per then per := max_per;
channels[chn].per := per;
gussetfreq(chn,periods[per]);
end;
3 : begin {Port to}
if channels[chn].per < channels[chn].dper then begin
w := channels[chn].dper;
per := channels[chn].per;
inc(per,channels[chn].fx_sl2);
if per > w then per := w;
if per > max_per then per := max_per;
if per < min_per then per := min_per;
channels[chn].per := per;
gussetfreq(chn,periods[per]);
end
else begin
w := channels[chn].dper;
per := channels[chn].per;
if per-channels[chn].fx_sl2 > per then per := min_per
else dec(per,channels[chn].fx_sl2);
if per < w then per := w;
if per < min_per then per := min_per;
if per > max_per then per := max_per;
channels[chn].per := per;
gussetfreq(chn,periods[per]);
end;
end;
4 : begin {vibrato}
_fxdata := channels[chn].fx_vib;
b := _fxdata and 15;
s := vib_tbl[channels[chn].vib_wave,channels[chn].vib_cnt];
s := (s * b) div 64;
w := channels[chn].per+s;
if w > max_per then w := max_per;
if w < min_per then w := min_per;
b := _fxdata shr 4;
gussetfreq(chn,periods[w]);
inc(channels[chn].vib_cnt,b);
if channels[chn].vib_cnt > 63 then
channels[chn].vib_cnt := channels[chn].vib_cnt - 64;
end;
5 : begin {volume slide & portamento}
if _fxdata and 15 > 0 then begin {slide down}
b := channels[chn].vol;
if b-_fxdata >= 0 then dec(b,_fxdata)
else b := 0;
if b > 128 then b := 0;
channels[chn].vol := b;
channels[chn].bar := b;
channels[chn].gvol := gusvol[b]*amp_vol+20000;
gussetvolume(chn,channels[chn].gvol);
end
else begin {slide up}
b := channels[chn].vol;
inc(b,_fxdata shr 4);
if b > 64 then b := 64;
channels[chn].vol := b;
channels[chn].bar := b;
channels[chn].gvol := gusvol[b]*amp_vol+20000;
gussetvolume(chn,channels[chn].gvol);
end;
_fxdata := channels[chn].fx_sl2;
if channels[chn].per < channels[chn].dper then begin {port to}
w := channels[chn].dper;
per := channels[chn].per;
inc(per,_fxdata);
if per > w then per := w;
if per > max_per then per := max_per;
if per < min_per then per := min_per;
channels[chn].per := per;
gussetfreq(chn,periods[per]);
end
else begin
w := channels[chn].dper;
per := channels[chn].per;
if per-_fxdata > per then per := min_per
else dec(per,_fxdata);
if per < w then per := w;
if per < min_per then per := min_per;
if per > max_per then per := max_per;
channels[chn].per := per;
gussetfreq(chn,periods[per]);
end;
end;
6 : begin {vibrato & vol slide}
begin
b := channels[chn].fx_vib and 15;
s := vib_tbl[channels[chn].vib_wave,channels[chn].vib_cnt];
s := (s * b) div 64;
w := channels[chn].per+s;
if w > max_per then w := max_per;
if w < min_per then w := min_per;
b := channels[chn].fx_vib shr 4;
gussetfreq(chn,periods[w]);
inc(channels[chn].vib_cnt,b);
if channels[chn].vib_cnt > 63 then
channels[chn].vib_cnt := channels[chn].vib_cnt - 64;
end;
{volume slide}
if _fxdata and 15 > 0 then begin {slide down}
b := channels[chn].vol;
if b-_fxdata >= 0 then dec(b,_fxdata)
else b := 0;
if b > 128 then b := 0;
channels[chn].vol := b;
channels[chn].bar := b;
channels[chn].gvol := gusvol[b]*amp_vol+20000;
gussetvolume(chn,channels[chn].gvol);
end
else begin {slide up}
b := channels[chn].vol;
inc(b,_fxdata shr 4);
if b > 64 then b := 64;
channels[chn].vol := b;
channels[chn].bar := b;
channels[chn].gvol := gusvol[b]*amp_vol+20000;
gussetvolume(chn,channels[chn].gvol);
end;
end;
$a : begin {volume slide}
if _fxdata and 15 > 0 then begin {slide down}
b := channels[chn].vol;
if b < (_fxdata and 15) then b := 0
else dec(b,_fxdata and 15);
if b > 64 then b := 0;
channels[chn].vol := b;
channels[chn].bar := b;
channels[chn].gvol := gusvol[b]*amp_vol+20000;
gussetvolume(chn,channels[chn].gvol);
end
else begin {slide up}
b := channels[chn].vol;
inc(b,_fxdata shr 4);
if b > 64 then b := 64;
channels[chn].vol := b;
channels[chn].bar := b;
channels[chn].gvol := gusvol[b]*amp_vol+20000;
gussetvolume(chn,channels[chn].gvol);
end;
end;
$e : begin
_efx := _fxdata shr 4;
_efxdata := _fxdata and 15;
case _efx of
9 : begin {Retrig note}
b := channels[chn].sample;
dec(channels[chn].trig_cnt);
if channels[chn].trig_cnt = 0 then begin
gussetofs(chn,gus_addr[b]+2);
channels[chn].trig_cnt := _efxdata;
end;
end;
$c : if _efxdata = 0 then begin {note cut}
gussetvolume(chn,0);
channels[chn].gvol := 0;
end
else begin
dec(_efxdata);
b := _fxdata;
b := b and $f0;
b := b or _efxdata;
channels[chn].fxdata := b;
end;
$d : begin {note delay}
channels[chn].start_fx := 255;
w := channels[chn].sample;
if channels[chn].on = 1 then begin
channels[chn].gvol :=
gusvol[channels[chn].vol]*amp_vol+20000;
channels[chn].hit := 1;
gussetbalance(chn,channels[chn].pan);
if (samples[w].loopend > 2) then
gusplayall(chn,8,gus_addr[w]+2,
gus_addr[w]+samples[w].loopstart,
gus_addr[w]+samples[w].loopend-1,
periods[channels[chn].per],
channels[chn].gvol)
else gusplayall(chn,0,gus_addr[w]+2,
gus_addr[w],
gus_addr[w]+samples[w].length+1,
periods[channels[chn].per],
channels[chn].gvol);
end;
end;
end;
end;
end;
end;
end;
procedure updatenotes;
var
n,cptn : integer;
begin
if cur_ptn >= header.length then new_ptn := 0;
cur_ptn := new_ptn;
cur_row := new_row;
if (cur_tick >= speed) and (speed > 0) then begin
speed := nspeed;
cur_tick := 0;
if jump = 0 then inc(cur_row);
if cur_row > 63 then begin
inc(cur_ptn);
cur_row := 0;
if cur_ptn > header.length-1 then begin
new_ptn := 0;
cur_ptn := 0;
end;
end;
end;
cptn := orders[cur_ptn];
new_ptn := cur_ptn;
new_row := cur_row;
if speed > 0 then begin
for n := 0 to maxchn-1 do begin
if channels[n].bar > 1 then dec(channels[n].bar,2)
else channels[n].bar := 0;
end;
inc(cur_tick);
if cur_tick = 1 then begin
virt_needptn(cptn);
get_notes;
get_fx;
virt_noneedptn(cptn);
end;
do_fx;
end;
if new_ptn <> cur_ptn then virt_warnptn(orders[new_ptn])
else if cur_row = 63 then begin
cptn := cur_ptn+1;
if cptn > header.length-1 then cptn := 0;
cptn := orders[cptn];
virt_warnptn(cptn);
end;
if jump = 1 then virt_warnptn(orders[new_ptn]);
end;
procedure volrampend;
var
chn : integer;
begin
for chn := 0 to header.chns-1 do begin
port[active_voice] := chn;
port[command] := $8d;
if port[data_high] and 3 = 1 then begin
port[command] := $d;
port[data_high] := 2;
port[command] := 9;
portw[data_low] := channels[chn].gvol;
end;
end;
end;
procedure modint; interrupt;
begin
volrampend;
dec(timer_cnt);
inc(time_counter3);
if timer_cnt < 1 then begin
inc(time_counter2);
updatenotes;
timer_cnt := timer_rate;
end;
asm sti end;
o_int_tick := int_tick;
int_tick := int_tick + int_rate;
if o_int_tick > int_tick then begin
inc(time_counter);
asm
pushf
cli
call oldint
end;
end
else
asm
mov al,20h
out 20h,al {send EOI}
end;
end;
{$s-}
{$f+}
procedure def_virt_alloc(numptn,ptnsize : integer);
var
n : integer;
begin
for n := 0 to 128 do patterns[n] := nil;
virt_info.numptn := numptn;
virt_info.ptnsize := ptnsize;
virt_info.err_wptn := -1;
virt_info.err_nptn := -1;
end;
procedure def_virt_free;
var
n : integer;
begin
for n := 0 to 128 do if patterns[n] <> nil then begin
freemem(patterns[n],virt_info.ptnsize);
patterns[n] := nil;
end;
end;
procedure def_virt_allocptn(ptn : integer);
begin
getmem(patterns[ptn],virt_info.ptnsize);
end;
procedure def_virt_loadptn(ptn : integer;p : pointer);
begin
move(p^,patterns[ptn]^,virt_info.ptnsize);
end;
procedure def_virt_freeptn(ptn : integer);
begin
if patterns[ptn] <> nil then begin
freemem(patterns[ptn],virt_info.ptnsize);
patterns[ptn] := nil;
end;
end;
function def_virt_getptn(ptn : integer) : pointer;
begin
def_virt_getptn := patterns[ptn];
end;
procedure def_virt_warnptn(ptn : integer);
begin
virt_info.warnedptn := ptn;
end;
procedure def_virt_needptn(ptn : integer);
begin
if ptn <> virt_info.warnedptn then begin
virt_info.err_cptn := cur_ptn;
virt_info.err_wptn := virt_info.warnedptn;
virt_info.err_nptn := ptn;
end;
end;
procedure def_virt_noneedptn(ptn : integer);
begin
end;
{$f-}
{$s-}
function heaperr(size : word) : integer; far;
begin
if size > 0 then begin
mod_error := 3;
heaperr := 1;
end;
end;
procedure load_MOD(s : string;debug : boolean);
var
f : file;
mbuf : pointer;
oldheaperr : procedure;
procedure set_up_modheader;
var
chn,c,n : integer;
begin
header.samples := 31;
header.name[0] := #20;
move(misc_buf^[0],header.name[1],20);
header.tag := ' ';
move(misc_buf^[1080],header.tag,4);
chn := maxchn;
with header do
if tag = 'M.K.' then chn := 4
else if tag = 'M!K!' then chn := 4
else if tag[1]+tag[2]+tag[3]='CHN' then begin
val(tag[0],n,c);
if c=0 then chn := n;
end
else if tag[2]+tag[3]='CH' then begin
val(tag[0]+tag[1],n,c);
if c=0 then chn := n;
end
else begin
header.samples := 15;
chn := 4;
end;
if chn > maxchn then begin
mod_error := 1;
exit;
end;
if header.samples = 15 then begin
move(misc_buf^[472],orders[0],128);
seek(f,600);
header.length := misc_buf^[470];
header.chns := 4;
end else begin
header.length := misc_buf^[950];
move(misc_buf^[952],orders[0],128);
if debug then writeln('Tag: ',header.tag);
end;
header.chns := chn;
max_ptn := 0;
for n := 0 to 127 do if orders[n] > max_ptn then begin
if orders[n] > 127 then begin
mod_error := 2;
exit;
end else max_ptn := orders[n];
end;
max_ptn := max_ptn+1;
end;
procedure mod_sample_info;
var
n : integer;
maxi : integer;
begin
for n := 0 to 31 do begin
fillchar(samples[n].name,sizeof(samples[n].name),0);
samples[n].length := 0;
samples[n].ftune := 0;
samples[n].volume := 0;
samples[n].loopstart := 0;
samples[n].loopend := 0;
end;
for n := 1 to header.samples do begin
move(misc_buf^[(n-1)*30+20],samples[n].name[1],22);
samples[n].name[23] := #0;
samples[n].length := 2*swap(misc_buf2^[(n-1)*15+21]); {n*30+42}
samples[n].ftune := misc_buf^[(n-1)*30+44];
samples[n].volume := misc_buf^[(n-1)*30+45];
samples[n].loopstart := 2*swap(misc_buf2^[(n-1)*15+23]); {n*30+46}
samples[n].loopend := 2*swap(misc_buf2^[(n-1)*15+24]); {n*30+48}
if samples[n].loopend < 3 then begin
samples[n].loopend := 0;
samples[n].loopstart := 0;
end;
inc(samples[n].loopend,samples[n].loopstart);
if samples[n].loopend > samples[n].length then
samples[n].loopend := samples[n].length;
end;
end;
procedure read_ptn(n : word);
var
row,note : integer;
w,w2,i : word;
b : byte;
mchn : byte;
mb : p_pattern;
begin
mchn := header.chns;
mb := mbuf;
blockread(f,misc_buf^,256*mchn);
for row := 0 to 63 do
for note := 0 to mchn-1 do begin
w := misc_buf2^[row*(2*mchn)+note*2];
w2 := misc_buf2^[row*(2*mchn)+note*2+1];
asm
mov cx,w
and cl,15
xchg cl,ch
and cx,0fffh
mov i,cx
end;
mb^[row*header.chns+note].per := i;
asm
mov al,byte ptr w2
shr al,4
mov ah,byte ptr w
and ah,11110000b
or al,ah
xor ah,ah
mov i,ax
end;
mb^[row*header.chns+note].sample := i;
mb^[row*header.chns+note].fx := lo(w2) and 15;
mb^[row*header.chns+note].fxdata := hi(w2);
i := mb^[row*header.chns+note].per;
w := 0;
repeat
inc(w);
until (w > 48) or (i = per_table[0,w]);
if w <= 48 then mb^[row*header.chns+note].note := w
else mb^[row*header.chns+note].note := 0;
end;
end;
procedure load_patterns;
var
num_ptn : longint;
n : word;
m_ptn : integer;
begin
if debug then write('Loading patterns');
for n := 0 to max_ptn-1 do if mod_error = 0 then begin
if debug then write('.');
virt_allocptn(n);
if mod_error <> 0 then begin
virt_free;
exit;
end;
read_ptn(n);
virt_loadptn(n,mbuf);
end;
if debug then writeln;
end;
procedure load2gus(len : word);
var
{n : word;
addlo,addhi : word;}
l : longint;
begin
l := top_addr;
asm
mov di,len
mov si,word ptr misc_buf
mov es,word ptr misc_buf+2
mov cx,word ptr l {cx=addlo}
mov bx,word ptr l+2 {bx=addhi}
and bx,$ff
@@1:
mov dx,command {Port [command] := $43;}
mov al,43h
out dx,al
mov dx,data_low {Portw[data_low] := AddLo;}
mov ax,cx
out dx,ax
mov dx,command {Port [command] := $44;}
mov al,44h
out dx,al
mov dx,data_high
mov ax,bx
out dx,ax {Port [data_high] := AddHi;}
add cx,1 {inc(l,1);}
adc bx,0
mov dx,dram_io {Port [dram_io] := misc_buf^[n];}
mov al,es:[si]
out dx,al
inc si
dec di
jnz @@1
end;
inc(top_addr,len);
end;
procedure load_sample(num : word);
const
block = 4096;
var
n : longint;
w : word;
fl,l : word;
len : longint;
b : byte;
begin
if debug then write('.');
guspoke(top_addr,0);
guspoke(top_addr+1,0);
guspoke(top_addr+2,0);
inc(top_addr,2);
len := samples[num].length+top_addr;
if (len > gus_bank+$40000) and (top_addr < gus_bank+$40000) then begin
gus_bank := gus_bank+$40000;
top_addr := gus_bank;
end;
samples[num].addr := top_addr;
gus_addr[num] := top_addr;
if samples[num].length < 1 then begin
guspoke(top_addr,0);
guspoke(top_addr+1,0);
guspoke(top_addr+2,0);
inc(top_addr,2);
exit;
end;
fl := (samples[num].length) div block;
l := (samples[num].length) mod block;
if fl > 0 then for w := 1 to fl do begin
blockread(f,misc_buf^,block);
load2gus(block); {load in 4kb blocks}
end;
if l > 0 then begin
blockread(f,misc_buf^,l);
load2gus(l); {load remainder}
end;
if samples[num].loopend > 2 then begin
guspoke(top_addr,guspeek(gus_addr[num]+samples[num].loopstart));
{b := guspeek(top_addr-1);
guspoke(top_addr,b);}
guspoke(top_addr+1,guspeek(gus_addr[num]+samples[num].loopstart));
guspoke(gus_addr[num]+samples[num].loopend+1,
guspeek(gus_addr[num]+samples[num].loopstart));
guspoke(gus_addr[num]+samples[num].loopend,
guspeek(gus_addr[num]+samples[num].loopstart));
inc(top_addr,2);
end
else guspoke(top_addr,0);
end;
var
i : integer;
l : longint;
begin
@oldheaperr := heaperror;
heaperror := @heaperr;
mod_error := 0;
getmem(misc_buf,5000);
getmem(mbuf,6000);
if mod_error <> 0 then exit;
misc_buf2 := addr(misc_buf^);
gus_bank := 0;
assign(f,s);
{$i-}
reset(f,1);
blockread(f,misc_buf^,1084); {read module header}
i := ioresult;
if i <> 0 then begin
mod_error := 2;
freemem(mbuf,6000);
freemem(misc_buf,5000);
exit;
end;
set_up_modheader;
if mod_error <> 0 then begin
freemem(mbuf,6000);
freemem(misc_buf,5000);
exit;
end;
mod_sample_info;
virt_alloc(max_ptn,sizeof(t_note)*64*header.chns);
load_patterns;
if mod_error <> 0 then begin
freemem(mbuf,6000);
freemem(misc_buf,5000);
exit;
end;
if debug then write('Loading samples');
for i := 0 to 31 do load_sample(i);
if debug then writeln;
close(f);
{$i+}
freemem(mbuf,6000);
freemem(misc_buf,5000);
loaded := true;
heaperror := @oldheaperr;
end;
procedure free_mod;
var
n,i : word;
begin
if playing then stop_playing;
if not loaded then exit;
loaded := false;
virt_free;
top_addr := 16;
for n := 0 to 31 do with samples[n] do begin
addr := 0;
for i := 0 to sizeof(name) do name[i] := #0;
length := 0;
loopstart := 0;
loopend := 0;
ftune := 0;
volume := 0;
end;
gus_bank := 0;
end;
procedure goto_mod(ptn,row : integer);
begin
jump := 1;
if ptn > header.length-1 then ptn := header.length;
if ptn < 0 then ptn := 0;
new_ptn := ptn;
new_row := row;
virt_warnptn(orders[ptn]);
end;
procedure initchn(chn : integer);
begin
channels[chn].vol := 0;
channels[chn].per := 428;
channels[chn].note := 13;
channels[chn].sample := 0;
channels[chn].pan := 7; {middle}
channels[chn].on := 1;
channels[chn].dper := 428;
channels[chn].bar := 0;
channels[chn].fx := 255;
channels[chn].fxdata := 0;
channels[chn].fx_sl2 := 0;
channels[chn].fx_vib := 0;
channels[chn].fx_portu := 0;
channels[chn].fx_portd := 0;
channels[chn].fx_trm := 0;
channels[chn].vib_cnt := 0;
channels[chn].vib_wave := 0;
channels[chn].hit := 0;
channels[chn].no_fx := 0;
channels[chn].start_fx := 0;
channels[chn].arp1 := 0;
channels[chn].arp2 := 0;
channels[chn].arp_cnt := 0;
channels[chn].gvol := 0;
end;
procedure init_mod;
var
n,i : integer;
l : longint;
begin
virt_info.err_wptn := -1;
virt_info.err_nptn := -1;
virt_info.err_cptn := -1;
virt_error := 0;
virt_alloc := def_virt_alloc;
virt_free := def_virt_free;
virt_allocptn := def_virt_allocptn;
virt_loadptn := def_virt_loadptn;
virt_freeptn := def_virt_freeptn;
virt_getptn := def_virt_getptn;
virt_warnptn := def_virt_warnptn;
virt_needptn := def_virt_needptn;
virt_noneedptn := def_virt_noneedptn;
for n := 10 to 1050 do begin
{gusperiod:=586580935 div (amigaperiod * (divisor div 100 shl 4))}
{divisor = 44100}
l := n;
l := 586580935 div (l * 7056);
periods[n] := l;
{hz = 7093789.2/(per*2)}
end;
for n := 0 to 255 do orders[n] := 0;
for n := 0 to maxchn-1 do begin
initchn(n);
gussetbalance(n,channels[n].pan);
end;
for n := 0 to 31 do with samples[n] do begin
addr := 0;
for i := 0 to sizeof(name) do name[i] := #0;
length := 0;
loopstart := 0;
loopend := 0;
ftune := 0;
volume := 0;
end;
for n := 0 to 13 do gussetvolume(n,0);
for n := 0 to 13 do gusstopvoice(n);
for n := 0 to 13 do gussetbalance(n,7);
fillchar(header,sizeof(header),0);
header.chns := 4;
playing := false;
loaded := false;
cur_ptn := 0;
cur_row := 0;
new_ptn := 0;
new_row := 0;
cur_tick := 0;
for n := 0 to 31 do guspoke(n,0);
top_addr := 16;
gus_bank := 0;
vblank := false;
getintvec(8,@oldint);
end;
{$s-}
procedure set_timer(ticks : word);
begin
asm cli end;
port[$43] := $36;
port[$40] := lo(ticks);
port[$40] := hi(ticks);
asm sti end;
end;
procedure stop_playing;
var
n : integer;
begin
int_rate := 65535;
set_timer(65535);
setintvec(8,@oldint);
for n := 0 to maxchn-1 do GusStopVoice(n);
for n := 0 to maxchn-1 do begin
channels[n].hit := 0;
channels[n].bar := 0;
end;
playing := false;
end;
procedure start_playing;
var
n : integer;
begin
if not loaded then exit;
playing := true;
for n := 0 to maxchn-1 do initchn(n);
speed := 6;
nspeed := 6;
tempo := 125;
channels[0].pan := 7-def_pan;
channels[1].pan := 8+def_pan;
channels[2].pan := 8+def_pan;
channels[3].pan := 7-def_pan;
if maxchn > 4 then for n := 4 to maxchn-1 do
channels[n].pan := channels[n-4].pan;
if maxchn > 8 then for n := 8 to maxchn-1 do
channels[n].pan := channels[n-8].pan;
for n := 0 to maxchn-1 do gussetbalance(n,channels[n].pan);
jump := 0;
int_tick := 0;
cur_ptn := 0;
cur_row := 0;
new_ptn := 0;
new_row := 0;
cur_tick := 0;
time_counter := 0;
time_counter2 := 0;
virt_warnptn(orders[0]);
virt_needptn(orders[0]);
asm cli end;
setintvec(8,@modint);
timer_rate := 20;
timer_cnt := 20;
int_rate := 1193182 div 1000;
set_timer(int_rate);
asm sti end;
end;
begin
end.